package top.jybill.interceptor;


import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import top.jybill.domain.MyUser;
import top.jybill.domain.ResponseData;
import top.jybill.domain.Role;
import top.jybill.exception.MyException;
import top.jybill.utils.JwtUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RequestInterceptor implements HandlerInterceptor {

  @Autowired
  private ResponseData res;
  private final static String ALLOWED[] = {"ADMIN", "USER"};

  /**
   * 请求之前 验证token 登陆请求无需验证其他都必须要token
   * @param request
   * @param response
   * @param handler
   * @return
   * @throws Exception
   */
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    // 初始化、清理一些对象
    res.clean();
    ObjectMapper mapper = new ObjectMapper();

    // 获取token
    String authorization;
    try {
      authorization = request.getHeader("Authorization").replace(JwtUtil.TOKEN_PREFIX, "");
    } catch (Exception e) {
      throw new MyException("authorization权限为空");
    }

    // token过期或无效
    if (!rightToken(authorization, request, response, mapper)) { return false; }

    // 账户是否有权限访问
      // 管理员直接通过全部可访问 用户只可访问不敏感信息
    if (!isAllowed(authorization, request, response, mapper)) { return false; }

    return true;
  }


  /**
   * token未过期且正确 返回true
   * @param authorization
   * @param response
   * @param mapper
   * @return
   * @throws Exception
   */
  private boolean rightToken(String authorization,
 HttpServletRequest request, HttpServletResponse response, ObjectMapper mapper)
          throws Exception {
    if (JwtUtil.isExpired(authorization) || JwtUtil.isExpired(authorization)) {
      res.metaPut("status", 401);
      res.metaPut("msg", "token过期或无效token");
      res.dataPut("redirect", "login");
      response.getWriter().write(mapper.writeValueAsString(res));
      return false;
    } else if (request.getSession().getServletContext().getAttribute(JwtUtil.TOKEN_PREFIX + authorization) == null) {
      res.metaPut("status", 400);
      res.metaPut("msg", "token失效请重新登录");
      res.dataPut("redirect", "login");
      response.getWriter().write(mapper.writeValueAsString(res));
      return false;
    }
    return true;
  }


  /**
   * 该角色是否激活 -> 是否允许访问 全部通过为true
   * @param authorization
   * @param response
   * @param mapper
   * @return
   * @throws Exception
   */
  private boolean isAllowed(String authorization,HttpServletRequest request, HttpServletResponse response,
                            ObjectMapper mapper) throws Exception {
    // 允许通过的角色
    List<String> roleList = new ArrayList();
    // token内容
    List roles = getMap(authorization, "roles", List.class);
    for (Object o: roles) {
      roleList.add(((Map)o).get("roleName").toString());
    }

    // 管理员
    if (ArrayUtils.contains(roleList.toArray(), ALLOWED[0])) {
      return true;
    }
//    System.out.println(roleList);
    if (!isNormalUserAllowed(authorization, request)) {
      res.metaPut("status", 400);
      res.metaPut("msg", "权限不足");
      response.getWriter().write(mapper.writeValueAsString(res));
      return false;
    }
    return true;
  }

  /**
   * 判断用户是否有权限访问接口 true有权限
   * @param authorization
   * @param request
   * @return
   * @throws Exception
   */
  private boolean isNormalUserAllowed(String authorization, HttpServletRequest request) throws Exception {
    Map permissions = getMap(authorization, "permissions", Map.class);
    String val = (String) permissions.get(request.getServletPath());
    return val != null ? true : false;
  }

  /**
   * 提取公共方法: 将token转成指定的key
   * @return
   */
  private <T> T getMap(String authorization, String key, Class<T> clazz) {
    Object keyObj = JwtUtil.getClaim(authorization).get(key);
    return clazz.cast(keyObj);
  }

}
